#pragma once
#include <coroutine>
#include <utility>

template <class A>
concept Awaiter = requires(A a, std::coroutine_handle<> h) {
  { a.await_ready() };
  { a.await_suspend(h) };
  { a.await_resume() };
};

template <class A>
concept Awaitable = Awaiter<A> || requires(A a) {
  { a.operator co_await() } -> Awaiter;
};

template <class T = void> struct NonVoidHelper {
  using Type = T;
};

template <> struct NonVoidHelper<void> {
  using Type = NonVoidHelper;

  explicit NonVoidHelper() = default;
};

template <class A> struct AwaitableTraits;

template <Awaiter A> struct AwaitableTraits<A> {
  using RetType = decltype(std::declval<A>().await_resume());
  using NonVoidRetType = NonVoidHelper<RetType>::Type;
};

template <class A>
  requires(!Awaiter<A> && Awaitable<A>)
struct AwaitableTraits<A>
    : AwaitableTraits<decltype(std::declval<A>().operator co_await())> {};

template <class T> class UnInit {
public:
  UnInit() noexcept {}
  ~UnInit() noexcept {}
  template <class... Args> void put(Args &&...args) {
    new (std::addressof(m_value)) T(std::forward<Args>(args)...);
  }
  T move() {
    T val{std::move(m_value)};
    m_value.~T();
    // std::cout << "move1" << std::endl;
    return std::move(val);
  }

private:
  UnInit(UnInit &&) = delete;
  union {
    T m_value;
  };
};

template <> struct UnInit<void> {
  auto move() { return NonVoidHelper<>{}; }

  void put(NonVoidHelper<>) {}
};
